# -*- coding: utf-8 -*-
"""
@File:    receive_service.py
@Author:  jk.dong
@Desc:    程序的邮件接收服务
"""
import traceback
import logging
import re
import os
import glob
import time
import datetime

from docwork.service.base_service import BaseService
from docwork.service.parse_service import ParseService, check_directory_exist, TitleAnalyzerProxy, BODY_SOURCE_REGION
from docwork.service.import_service import DocImportService, check_suffix_compatible
from docwork.utils.crypto import decrypt
from docwork.config import table_km_spam, table_km_rawmaillog, table_km_customerinfo, Path, RUN_CONF
from docwork.dao.receive_dao import ReceiveDao, RECV_CONF, RECV_DB, deal_date


class ReceiveService(BaseService):
    """
    初始化邮箱服务器object，km_customerinfo表的单条数据
    """
    def __init__(self, name: str, thread_id: int, mailbox_config: dict):
        # 后续编写测试用例，可能需要重写__setattr__、__setitem__等方法
        super().__init__(name)
        self.t_id = thread_id
        self.mail_client = None
        self.mailbox_config = mailbox_config
        self.protocol = RECV_CONF['receive-setting'].get('recv_protocol')
        self.check_spam = RECV_CONF['receive-setting'].get('check_spam')
        self.recv_timeout = RECV_CONF['receive-setting'].get('recv_timeout')
        self.date_offset = RECV_CONF['receive-setting'].get('date-offset')
        self.body_source_region = RECV_CONF['receive-setting'].get('body-source')
        self.password_encrypt = RECV_CONF['receive-setting'].get('mailbox_encrypt')
        # 垃圾邮件
        self.spam = []
        self.date_now = datetime.datetime.now()

    def delete_mail(self, status: int, index, email_time=None, ews_mail=None):
        # 删除不同协议邮箱中的邮件
        if status == 1:
            if self.protocol == "EWS" and ews_mail:  # EWS协议删除
                self.mail_client._delete(index, ews_mail)
            elif self.protocol in ["POP3", "IMAP", "COREMAIL", "HTTPS"]:
                self.mail_client._delete(index)
            elif self.protocol == "EML":
                self.delete_file(filename=index[3:], directory=Path(RECV_CONF['receive-setting'].get('recv_eml_path')))
            else:
                logging.error(f"当前protocol: {self.protocol}不合规！")
        elif status > 1 and email_time:
            logging.info(f"检查删除邮件的延迟时间:{status}")
            expire_date = self.date_now - datetime.timedelta(status - 1)
            logging.info(f"延迟时间: {expire_date}\t邮件时间: {email_time}")
            if email_time <= expire_date:
                if self.protocol == "EWS" and ews_mail:
                    self.mail_client._delete(index, ews_mail)
                elif self.protocol in ["POP3", "IMAP", "COREMAIL", "HTTPS"]:
                    self.mail_client._delete(index)
                elif self.protocol == "EML":
                    self.delete_file(filename=index, directory=RECV_CONF['receive-setting'].get('recv_eml_path'))
                else:
                    logging.error(f"当前protocol: {self.protocol}不合规！")
            else:
                logging.info(f"邮件 No.{index} 未在延迟时间范围, 不删除.")
        else:
            logging.info(f"删除邮件配置项IsDelMail为:{status}, 不删除!")

    def filter_spam(self, msg_subject, msg_from, rawmaillog_id):
        """
        根据邮件标题和邮箱地址来判断是否为垃圾邮件并过滤
        :param msg_subject:
        :param msg_from:
        :param rawmaillog_id:
        :return: boolean 是否为过滤邮件
        """
        _is_spam = False
        if self.check_spam and self.spam:
            for dr in self.spam:
                # 邮件标题过滤
                if dr[0]:
                    if msg_subject.find(dr[0]) >= 0:
                        _is_spam = True
                        logging.info(
                            f"邮件标题: {msg_subject} 包含 {dr[0]}, 列为垃圾邮件.")
                        ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, -2)
                        self.save_local_rawmaillog(
                            f"{rawmaillog_id}#-2#{dr[0]}#{self.date_now.strftime('%Y-%m-%d %H:%M:%S')}")
                        break
                # 邮件地址过滤
                else:
                    if dr[1]:
                        logging.warning(f'km_spam表的当前subject数据字段为空！')
                        if msg_from.split('@')[0] == dr[1].split('@')[0]:
                            _is_spam = True
                            logging.info(
                                f"邮件来源: {msg_from} 包含 {dr[1]}, 列为垃圾邮件.")
                            ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, -2)
                            self.save_local_rawmaillog(
                                f"{rawmaillog_id}#-2#{dr[1]}#{self.date_now.strftime('%Y-%m-%d %H:%M:%S')}")
                            break
                    else:
                        logging.error('km_spam表当前行数据为空，请检查！')

        return _is_spam

    def save_local_rawmaillog(self, content):
        logging.info(f"filter mail info: {content}")
        filter_log_path = RECV_CONF['receive-setting'].get('filter_log_path')
        if not filter_log_path:
            return
        check_directory_exist(filter_log_path)
        filename = filter_log_path.joinpath(f'rawmail_thread{self.t_id}_{self.date_now.strftime("%Y%m%d")}.log')
        logging.info(f"filterlog_filename is: {filename}")
        with open(filename, 'a+') as f:
            f.write(content)
            f.write('\n')

    # 对准备插入km_rawmaillog表的邮件数据进行处理
    def raw_mail_process(self, rawmaillog_id, rawmail_id, num, index, item, parse_service, parse_type):
        """
        从邮箱服务器或eml文件中解析邮件信息
        return: status(continue or None)、loop_num(object:n)、msg
        """
        logging.info("Into raw mail process")
        isdelmail = int(self.mailbox_config['ISDELMAIL'])
        try:
            # 根据mailid从邮箱服务器中解析对应邮件信息
            if self.protocol != "EWS":
                if self.protocol == "COREMAIL" or self.protocol == "EML":
                    msg = parse_service.analysis_email(index=rawmail_id, rawmaillog_id=rawmaillog_id,
                                                       customer_id=int(self.mailbox_config['OBJID']),
                                                       parse_type=parse_type)
                else:
                    msg = parse_service.analysis_email(index=index, rawmaillog_id=rawmaillog_id,
                                                       customer_id=int(self.mailbox_config['OBJID']),
                                                       parse_type=parse_type)
            else:
                msg = parse_service.analysis_email(index=index, rawmaillog_id=rawmaillog_id,
                                                   customer_id=int(self.mailbox_config['OBJID']),
                                                   parse_type=parse_type, ews_mail=item)
            # 判断邮件解析是否成功
            if not msg or not msg.analyse_success:
                logging.warning("STOP:当前邮件解析为空或解析失败（func：analysis_email）")
                # 在解析失败时（可能是网络原因或程序bug），不对邮箱中的邮件进行删除
                if self.protocol == "COREMAIL" or self.protocol == "EML":
                    self.delete_mail(status=isdelmail, index=rawmail_id)
                else:
                    if self.protocol == "EWS" and item:
                        self.delete_mail(status=isdelmail, index=index, ews_mail=item)
                    self.delete_mail(status=isdelmail, index=index)
                # 邮件为空，则不再重复下载接收，此时要保留rawmaillog，保证下次不再下载
                logging.info("Continue ... ")
                return "continue", num, None

            # 邮件标题处理
            logging.info(f"邮件标题: {msg.subject}")
            if not msg.subject:
                msg.subject = "原始邮件标题为空"
                logging.warning(f"检测到邮件标题为空, 重置标题为==>{msg.subject}")
            else:
                if msg.subject.startswith('Spam: '):
                    logging.warning(f"原始邮件标题为：{msg.subject}\t去除Spam关键字")
                    msg.subject = msg.subject.replace('Spam: ', '')
                """邮件标题带有【】[]()（）,开头或结尾，中间的不算，并且里面含有关键词：外来，外部，邮件，警告，垃圾，通知。 过滤掉"""
                regex_token = r'[\[【(（][^(\[【\(（)]*[(外来|部)邮件警告垃圾通知]+[^(\]】\)）)]*[]】)）]'
                regex_compile = re.compile(f'^{regex_token}|{regex_token}$')
                filter_title = msg.subject
                if regex_compile.match(msg.subject):
                    logging.info('进行邮件标题的过滤...')
                    filter_title = regex_compile.sub(repl='', string=msg.subject)
                    logging.info(f"过滤后的标题为: {filter_title}")
                    msg.analysis_title = filter_title
                # 标题识别逻辑代码
                if RECV_CONF['receive-setting'].get('title-extract').get('switch'):
                    msg = TitleAnalyzerProxy(RECV_CONF['receive-setting'].get('title-extract').get('use_mode')).analyze_title(msg, filter_title)
                else:
                    msg.type_id = 24
                    msg.broker_id = 1000
                    msg.stk_code = ''
                    msg.researcher = ''
                    msg.invest_rank_origin = ''
                    msg.industry_rank_origin = ''
                    if not hasattr(msg, 'analysis_title'):
                        msg.analysis_title = msg.subject
                # 标题过长，截取200以内
                if len(msg.analysis_title.encode()) >= RECV_CONF["receive-setting"].get("max_attach_title"):
                    ind = RECV_CONF["receive-setting"].get("max_attach_title")
                    sub_bool = True
                    while sub_bool:
                        try:
                            msg.analysis_title = msg.analysis_title.encode()[:ind].decode()
                            sub_bool = False
                        except Exception as err:
                            logging.warning(f"解析后标题的编解码出现报错:{err}")
                            ind -= 1

            # 根据msg.from再查询一次km_rawmaillog表记录（猜测防止邮箱重置后，msgid又从1开始计算，导致判重失败）
            rawmaillog2 = ReceiveDao.get_rawmaillog(rawmail_id, msg.from_) if parse_type == 'server' else None
            # km_rawmaillog表的重复xinfo为0或1的情况，不再查询
            if rawmaillog2:
                logging.info("存在km_rawmailog表记录，根据rawmail_id和msg.from_的查询"
                             f"ID:{rawmaillog2.get('id')}\tFrom:{msg.from_}\tMailID:{rawmail_id}")
                if isdelmail:
                    if rawmaillog2.get('mailtime'):
                        if self.protocol == "COREMAIL" or self.protocol == "EML":
                            self.delete_mail(status=isdelmail, index=rawmail_id,
                                             email_time=rawmaillog2.get('mailtime'))
                        else:
                            if self.protocol == "EWS" and item:
                                self.delete_mail(status=isdelmail, index=index,
                                                 email_time=rawmaillog2.get('mailtime'), ews_mail=item)
                            self.delete_mail(status=isdelmail, index=index, email_time=rawmaillog2.get('mailtime'))
                    else:
                        logging.info("km_rawmaillog表无mailtime字段，重新解析邮件信息来获取接收时间")
                        tmp_name = f'tmp{index}'
                        if self.protocol != "EWS":
                            if self.protocol == "COREMAIL":
                                msg_tmp = parse_service.analysis_email(index=rawmail_id, rawmaillog_id=tmp_name,
                                                                       customer_id=self.mailbox_config['OBJID'])
                            else:
                                msg_tmp = parse_service.analysis_email(index=index, rawmaillog_id=tmp_name,
                                                                       customer_id=self.mailbox_config['OBJID'])
                        else:
                            msg_tmp = parse_service.analysis_email(index=index, rawmaillog_id=tmp_name,
                                                                   customer_id=self.mailbox_config['OBJID'],
                                                                   ews_mail=item)
                        if not msg_tmp:
                            logging.warning("当前邮件解析为空")
                            if self.protocol == "COREMAIL" or self.protocol == "EML":
                                self.delete_mail(status=isdelmail, index=rawmail_id)
                            else:
                                if self.protocol == "EWS" and item:
                                    self.delete_mail(status=isdelmail, index=index, ews_mail=item)
                                self.delete_mail(status=isdelmail, index=index)
                            return 'continue', num, None
                        logging.info(f"邮件接收时间为:{msg_tmp.receive_time}")
                        if self.protocol == "COREMAIL" or self.protocol == "EML":
                            self.delete_mail(status=isdelmail, index=rawmail_id,
                                             email_time=msg_tmp.receive_time)
                        else:
                            if self.protocol == "EWS" and item:
                                self.delete_mail(status=isdelmail, index=index,
                                                 email_time=msg_tmp.receive_time, ews_mail=item)
                            self.delete_mail(status=isdelmail, index=index,
                                             email_time=msg_tmp.receive_time)
                else:
                    if self.protocol == "EWS":
                        item.is_read = True
                        item.save(update_fields=['is_read'])
                        logging.info("set item to isread 2")
                    logging.info(f"IsDelMail:{isdelmail}, 不删除!")
                logging.info("Continue ... ")
                return "continue", num, None
            # logging.info(f"开始下载第{num + 1}封邮件/总{mail_count}封")
            logging.info(f"原始标题:{msg.subject}\t附件数量:{len(msg.attachments)}\t"
                         f"接收时间:{msg.receive_time}")
            # 邮件没有接收时间，则不再重复下载接收，保留rawmaillog，保证下次不再下载
            if not msg.receive_time:
                if self.protocol == "COREMAIL" or self.protocol == "EML":
                    self.delete_mail(status=isdelmail, index=rawmail_id)
                else:
                    if self.protocol == "EWS" and item:
                        self.delete_mail(status=isdelmail, index=index, ews_mail=item)
                    self.delete_mail(status=isdelmail, index=index)
                logging.warning("当前邮件无接收时间. Continue ...")
                return "continue", num, None
            if self.date_offset['limit_date_offset'] > 0:
                start_date = self.date_now - datetime.timedelta(days=int(self.date_offset['limit_date_offset']))
                logging.info(f"配置项LimitDateOffset:{self.date_offset['limit_date_offset']}\tStartDate:{start_date}\t"
                             f"MailDate:{msg.receive_time}")
                if msg.receive_time < start_date:
                    logging.info(f"当前邮件接收时间已过期 1. Subject:{msg.subject}")
                    if self.protocol == "COREMAIL" or self.protocol == "EML":
                        self.delete_mail(status=isdelmail, index=rawmail_id)
                    else:
                        if self.protocol == "EWS" and item:
                            self.delete_mail(status=isdelmail, index=index, ews_mail=item)
                        self.delete_mail(status=isdelmail, index=index)
                    # 只接收提前LimitDateOffset天的邮件，故要删除km_rawmaillog表已有的记录
                    ReceiveDao.delete_rawmaillog(rawmaillog_id)
                    if self.date_offset['limit_continue_recv']:
                        logging.info("不存在配置项limittimecontinuerecv. Break ... ")
                        return "break", num, None
                    logging.warning("Continue ... ")
                    return "continue", num, None
                else:
                    logging.info("当前邮件接收时间未过期")
            if self.date_offset['limit_time_start']:
                start_date = datetime.datetime.strptime(self.date_offset['limit_time_start'],
                                                        '%Y-%m-%d %H:%M:%S')
                logging.info(f"配置项limittimestart:{self.date_offset['limit_time_start']}\n"
                             f"StartDate:{start_date}\tMailDate:{msg.receive_time}")
                if msg.receive_time < start_date:
                    logging.info(f"当前邮件接收时间已过期 2. Subject:{msg.subject}")
                    if self.protocol == "COREMAIL" or self.protocol == "EML":
                        self.delete_mail(status=isdelmail, index=rawmail_id)
                    else:
                        if self.protocol == "EWS" and item:
                            self.delete_mail(status=isdelmail, index=index, ews_mail=item)
                        self.delete_mail(status=isdelmail, index=index)
                    ReceiveDao.delete_rawmaillog(rawmaillog_id)
                    if self.date_offset['limit_continue_recv']:
                        logging.info("不存在配置项limittimecontinuerecv. Break ... ")
                        return "break", num, None
                    logging.warning("km_rawmaillog记录已被删除. Continue ... ")
                    return "continue", num, None
            if msg.receive_time > self.date_now + datetime.timedelta(minutes=120):
                logging.info(f"当前邮件接收时间大于当前系统时间, mail date:{msg.receive_time}\t"
                             f"current date:{self.date_now}, 重置邮件接收时间为当前系统时间.")
                msg.receive_time = self.date_now
            # 邮件查重
            if len(msg.attachments) > 0:
                # 检查有效附件,if not validate. msg.attachments.pop(index)
                valid_count = 0
                for att_i in range(len(msg.attachments) - 1, -1, -1):
                    _ain = msg.attachments[att_i].get('attachName').lower()
                    _ain_path = msg.attachments[att_i].get('path')
                    if _ain == 'recvinfo.xml':
                        logging.info("属于服务器端收到的报告，不检查标题重复")
                    if not check_suffix_compatible(_ain,
                        ['doc', 'docx', 'xls', 'xlsx', 'ppt',
                         'pptx', 'pdf', 'xml', 'html', 'rar', 'zip', 'dat']):
                        logging.warning(f"该附件为无效文件，移除附件:{_ain}")
                        msg.attachments.pop(att_i)
                        os.remove(_ain_path)
                        continue
                    if check_suffix_compatible(_ain,
                                            ['doc', 'docx', 'xls', 'xlsx', 'ppt',
                                             'pptx', 'pdf']):
                        valid_count += 1
                if valid_count < 1:
                    logging.info("有效附件小于1, 根据标题和邮件时间来查重。")
                    mail_title = ReceiveDao.get_similar_mail(msg.subject)
                    if mail_title or (msg.subject == "原始邮件标题为空"):
                        logging.warning(
                            f"STOP:存在相同标题. Title:{msg.subject}. Continue ... ")
                        ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, -1)
                        if self.protocol == "COREMAIL" or self.protocol == "EML":
                            self.delete_mail(status=isdelmail, index=rawmail_id, email_time=msg.receive_time)
                        else:
                            if self.protocol == "EWS" and item:
                                self.delete_mail(status=isdelmail, index=index,
                                                 email_time=msg.receive_time, ews_mail=item)
                            self.delete_mail(status=isdelmail, index=index, email_time=msg.receive_time)
                        return 'continue', num, None
                    else:
                        logging.info("邮件不重复!")
            else:
                logging.info("当前邮件无附件，根据标题和邮件时间来查重。")
                mail_title = ReceiveDao.get_similar_mail(msg.subject)
                if mail_title:
                    logging.warning(
                        f"STOP:存在相同标题. Title:{msg.subject}. Continue ... ")
                    ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, -1)
                    if self.protocol == "COREMAIL" or self.protocol == "EML":
                        self.delete_mail(status=isdelmail, index=rawmail_id, email_time=msg.receive_time)
                    else:
                        if self.protocol == "EWS" and item:
                            self.delete_mail(status=isdelmail, index=index,
                                             email_time=msg.receive_time, ews_mail=item)
                        self.delete_mail(status=isdelmail, index=index, email_time=msg.receive_time)
                    # 邮件标题重复，则不再重复下载接收，此时要保留rawmaillog,保证下次不再下载
                    return 'continue', num, None
            # insert mail receive log
            try:
                # _subject = msg.subject.replace("'", "")
                logging.info(f"更新km_rawmaillog表中当前邮件的记录（邮件原始标题、邮件来源和发送地址）")
                ReceiveDao.update_rawmaillog(rawmail_id, rawmaillog_id, msg)
                # 接收完毕，将km_rawmaillog表的XINFO设为1
                ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, 1)
                # 过滤垃圾邮件并判断是否要删除
                is_spam = self.filter_spam(msg_subject=msg.subject, msg_from=msg.from_, rawmaillog_id=rawmaillog_id)
                if RECV_CONF["receive-setting"].get('delete_spam') and is_spam:
                    isdelmail = 1
                if self.protocol == "COREMAIL" or self.protocol == "EML":
                    self.delete_mail(status=isdelmail, index=rawmail_id,
                                     email_time=msg.receive_time)
                else:
                    if self.protocol == "EWS" and item:
                        self.delete_mail(status=isdelmail, index=index,
                                         email_time=msg.receive_time, ews_mail=item)
                    else:
                        self.delete_mail(status=isdelmail, index=index,
                                         email_time=msg.receive_time)
                if is_spam:
                    logging.info(f"邮件 No.{index} 为垃圾邮件. Continue ... ")
                    # 将强制删除垃圾邮件的删除状态重新置为原始值
                    isdelmail = int(self.mailbox_config['ISDELMAIL'])
                    return 'continue', num, None
            except Exception as err:
                logging.info(traceback.format_exc())
                logging.error(f"保存当前邮件的记录或过滤删除垃圾邮件时发生异常. Error: {err}")
                ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, -10)
        except Exception as err:
            # 出现异常，则不再重复下载接收，此时要保留rawmaillog，保证下次不再下载
            # deleteRawMailLog(rawmaillogid)
            logging.info(traceback.format_exc())
            ReceiveDao.update_rawmaillog_xinfo(rawmaillog_id, -10)
            logging.error(f"STOP: GetMessage() 发生异常：{err}.")
            logging.info('Continue ...')
            return "continue", num, None
        # endregion

        logging.info(
            f"Mail received:{msg.message_id}\tMail subject:{msg.subject}\tMail Date:{msg.receive_time}")
        # endregion

        # region Save Mail
        # if self.protocol == "COREMAIL" or self.protocol == "EML":
        #     self.delete_mail(status=isdelmail, index=rawmail_id, email_time=msg.receive_time)
        # else:
        #     if self.protocol == "EWS" and item:
        #         self.delete_mail(status=isdelmail, index=index,
        #                          email_time=msg.receive_time, ews_mail=item)
        #     self.delete_mail(status=isdelmail, index=index, email_time=msg.receive_time)
        # endregion
        num += 1
        if not isdelmail:
            if self.protocol == "EWS":
                item.is_read = True
                item.save(update_fields=['is_read'])
            # elif self.protocol == "COREMAIL":
            #     # 设为已读
            #     if setread(server, user_name, pwd, rawmail_id):
            #         logging.info("Coremail协议邮件已读设置成功！")
            #     else:
            #         raise Exception("Coremail协议邮件已读设置失败")
            logging.info("设置当前邮件为已读状态")
            logging.info(f"IsDelMail:{isdelmail}, 邮件不删除!")

        return 'normal', num, msg

    @staticmethod
    def delete_file(filename, directory):
        for i in glob.glob(str(directory.joinpath(filename)) + ".*"):
            logging.info('delete file: ' + i)
            os.remove(i)

    @staticmethod
    def handle_docs_file(filepath, max_nums):
        docs_list = os.listdir(filepath)
        docs_list.sort(key=lambda x: x.split('.')[0])
        count_docs = len(docs_list)
        if count_docs <= int(max_nums):
            return
        logging.info(f"{filepath}路径下文件数（{count_docs}）大于保留数（{max_nums}），删除旧文件")
        handle_count = count_docs - int(max_nums)
        i = 0
        while i < handle_count:
            os.remove(filepath / docs_list[i])
            i += 1

    def recv_mails(self):
        msg_list = []
        try:
            # region Init mailbox config
            server = self.mailbox_config['SERVER']
            user = self.mailbox_config['USERNAME']
            pwd = self.mailbox_config['PASSWORD']
            port = self.mailbox_config['PORT']
            start_index = int(self.mailbox_config['STARTMAILNO'])
            isdelmail = int(self.mailbox_config['ISDELMAIL'])
            if str(self.mailbox_config['REMOVETAG']) == '1':
                logging.warning(f"UserName: {user}, RemoveTag: {self.mailbox_config['REMOVETAG']}为1，跳过接收")
                logging.info("Continue ... ")
                return "continue"
            if isdelmail < 0:
                logging.info("km_customerinfo表字段isdelmail<0，重置为0")
                isdelmail = 0
            # 列出当前threadid的km_customerinfo表的信息
            logging.info(f'km_customerinfo表信息==> server:{server}, port:{port}, username:{user}, password:***, '
                         f'protocol:{self.protocol},\n isdelmail:{isdelmail}, removetag:{self.mailbox_config["REMOVETAG"]}, '
                         f'batch number:{self.mailbox_config["BATCHRECEIVENUM"]}, startmailno:{start_index}')
            if (self.protocol.lower() == 'pop3' and port not in [110, 995]) or \
                    (self.protocol.lower() == 'imap' and port not in [143, 993]):
                logging.warning(
                    f'当前配置项protocol：{self.protocol}未使用常用端口，km_customerinfo邮箱配置表port字段为：{port}')
            if RECV_CONF['receive-setting'].get('mailbox_encrypt'):
                try:
                    logging.info('配置项mailbox_encrypt为True，对邮箱密码解密')
                    pwd = decrypt(pwd, config_=RUN_CONF['run-setting'])
                except Exception as err:
                    logging.warning(f"邮箱密码解密发生异常。Error:{err}")
                    logging.info("Continue ...")
                    return "continue"
            # HTML解析发送功能的km_customerinfo表objid白名单配置
            if BODY_SOURCE_REGION.get('customer_list') and (
                    self.mailbox_config['OBJID'] not in BODY_SOURCE_REGION.get('customer_list')):
                BODY_SOURCE_REGION['send'] = False
            # endregion
            # region Check Spam
            if self.check_spam:
                logging.info("配置了检查垃圾邮件")
                spam_sql = f"SELECT SUBJECT, MAILFROM FROM {table_km_spam}"
                self.spam = RECV_DB.search(spam_sql)
                if len(self.spam) > 0:
                    logging.info("km_spam表的数据为:")
                    for dr in self.spam:
                        _subject = dr[0]
                        _from = dr[1] or ''
                        logging.info(f"字段Subject:{_subject}\t字段MailFrom:{_from}")
                else:
                    logging.info("km_spam表无数据，不过滤垃圾邮件。")
            # endregion

            # region Connect mail server
            logging.info(f"尝试连接邮箱服务器, AuthenticationMethod.USERPASS")
            # 将连接邮箱服务器的参数传给parseService类初始化
            parse_service = ParseService()
            self.mail_client = parse_service.client(user, pwd, server, port, self.protocol, timeout=self.recv_timeout)
            if not self.mail_client:
                logging.error(f"Disconnect: 当前邮箱（{server}）连接失败")
                logging.info("Continue ... ")
                return "continue"
            logging.info(f"Connect: 邮箱服务器（{server}）响应成功")
            items = []
            mail_list = []
            if self.protocol != "EWS":
                mail_list = self.mail_client.get_message_uid()
                mail_count = len(mail_list)
            else:
                mail_count = self.mail_client.account.inbox.unread_count
                items = self.mail_client.account.inbox.filter(is_read=False).order_by('datetime_received')

            if not mail_count:
                logging.warning(
                    f"邮箱协议{self.protocol}，邮箱服务器{server}，无邮件记录，mail_count为{mail_count}.")
                self.mail_client.quit()
                logging.info("Continue ...")
                return "continue"

            logging.info(
                f"StartIndex:{start_index}, MailCount:{mail_count}, BatchNum:{self.mailbox_config['BATCHRECEIVENUM']}")
            if start_index <= mail_count:
                if start_index < 0:
                    start_index = 1
                    logging.warning(f"StartIndex < 0, 重置StartIndex为{start_index}")
                logging.info(f"StartIndex <= MailCount, 当前StartIndex为{start_index}")
            else:
                logging.warning(f"STOP:邮箱服务器{server} MailCount（{mail_count}）, 小于 StartIndex（{start_index}）")
                logging.info("Continue ... ")
                return 'continue'
            # endregion

            # region Mail server info
            loop_num = 0  # 根据batch num作为break的触发变量
            j = mail_count + 1
            msg_list = []
            logging.info("********** 开始循环处理邮箱中的邮件 **********")
            while j > start_index:
                j -= 1
                item = None
                if self.protocol != "EWS" and mail_list:
                    rawmail_id = mail_list[j - 1]
                else:
                    item = items[j - 1]
                    rawmail_id = item.id
                # 每次收取一定量的mail（根据batch receive number），然后退出等待下一次的收取
                if loop_num >= int(self.mailbox_config['BATCHRECEIVENUM']):
                    logging.info(
                        f"已接收处理邮件数：{loop_num}，大于等于BatchNum: {self.mailbox_config['BATCHRECEIVENUM']}")
                    logging.info("Break ...")
                    break
                # end
                # region Mail Break

                logging.info(f"进度条==={j}/{mail_count}, 原始邮件ID:{rawmail_id}")
                if not rawmail_id:
                    logging.warning("STOP:原始邮件ID为空。 Continue ... ")
                    continue

                rawmaillog = ReceiveDao.get_rawmaillog(rawmail_id)
                # 未执行对应操作的邮件(包括未接收的邮件和未识别邮件，所以else对应的是已收邮件)
                if not rawmaillog:
                    logging.info("km_rawmaillog表无记录，作为新邮件开始解析接收")
                    # region Get Message
                    logging.info(
                        f"开始下载, no:{j}/{mail_count}\t当前邮箱用户名:{user}")
                    rawmaillog_id = ReceiveDao.save_rawmaillog(rawmail_id)
                    if not rawmaillog_id:  # 保存km_rawmaillog失败，导致rawmaillog_id为None
                        logging.warning("STOP: km_rawmaillog表保存失败，请检查")
                        logging.info('Continue ... ')
                        continue
                    logging.info(f'当前邮件rawmaillog id为:{rawmaillog_id}')
                    # 进入邮件解析处理
                    if self.protocol == 'EML':
                        control_keyword, loop_num, msg = self.raw_mail_process(rawmaillog_id, rawmail_id=rawmail_id,
                                                                               num=loop_num, index=j, item=item,
                                                                               parse_service=parse_service,
                                                                               parse_type='eml')
                    else:
                        control_keyword, loop_num, msg = self.raw_mail_process(rawmaillog_id, rawmail_id=rawmail_id,
                                                                               num=loop_num, index=j, item=item,
                                                                               parse_service=parse_service, parse_type='server')
                    match control_keyword:
                        case 'continue':
                            continue
                        case 'break':
                            break
                        case 'normal':
                            msg_list.append(msg)
                            logging.info(f"<============= while loop {loop_num} end =============>")
                else:
                    logging.info("km_rawmaillog表存在记录")
                    logging.info(f"Mail index:{j}\tuid:{rawmail_id}")
                    logging.info(f"{table_km_rawmaillog}表信息:{rawmaillog}")

                    # nlp模式和isdelmail为True的情况下，对xinfo为0或1的邮件重新接收
                    if rawmaillog.get('XINFO') == '0' or rawmaillog.get('XINFO') == '1':
                        logging.info("xinfo为0或1，重新解析入库")
                        rawmaillog_id = rawmaillog.get('ID')
                        control_keyword, loop_num, msg = self.raw_mail_process(rawmaillog_id, rawmail_id=rawmail_id,
                                                                               num=loop_num, index=j, item=item,
                                                                               parse_service=parse_service, parse_type='file')
                        match control_keyword:
                            case 'continue':
                                continue
                            case 'break':
                                break
                            case 'normal':
                                msg_list.append(msg)
                                logging.info(f"<===== 'while loop' num:{loop_num + 1} end =====>")
                    else:
                        logging.info("km_rawmaillog表已有记录且xinfo字段正常，不入库")
                        # isdelmail>0的情况，非同一天的重复mailid并且报告标题不同，将mailid改为mailid_num来存
                        # if int(isdelmail) > 0:
                        #     rawmaillog_subject = rawmaillog.get('mailtime')
                        #     mail_time = rawmaillog.get('mailtime')
                        #     if isdelmail == 0:
                        #         offset_date = date_now - mail_time
                        # region Delete Mail From Server
                        try:
                            # 已接收邮件中的可识别邮件如果要删除，就删除（每次都有此操作是为了删除无效产生）
                            if isdelmail:
                                mail_time = rawmaillog.get('mailtime')
                                if mail_time:
                                    if self.protocol == "COREMAIL" or self.protocol == "EML":
                                        self.delete_mail(status=isdelmail, index=rawmail_id,
                                                         email_time=mail_time)
                                    else:
                                        if self.protocol == "EWS" and item:
                                            self.delete_mail(status=isdelmail, index=j,
                                                             email_time=mail_time, ews_mail=item)
                                        self.delete_mail(status=isdelmail, index=j, email_time=mail_time)
                                # 若km_rawmaillog表无mailtime字段，需重新从服务器中解析获取邮件信息，来删除
                                else:
                                    logging.info("km_rawmaillog表无mailtime字段, 重新从服务器中解析获取邮件信息")
                                    tmp_name = f'tmp{j}'
                                    if self.protocol != "EWS":
                                        if self.protocol == "COREMAIL":
                                            msg_tmp = parse_service.analysis_email(rawmail_id, tmp_name,
                                                                                   self.mailbox_config['OBJID'])
                                        else:
                                            msg_tmp = parse_service.analysis_email(j, tmp_name,
                                                                                   self.mailbox_config['OBJID'])
                                    else:
                                        msg_tmp = parse_service.analysis_email(j, tmp_name,
                                                                               self.mailbox_config['OBJID'],
                                                                               item)
                                    if not msg_tmp:
                                        logging.warning("当前邮件解析为空")
                                        if self.protocol == "COREMAIL" or self.protocol == "EML":
                                            self.delete_mail(status=isdelmail, index=rawmail_id)
                                        else:
                                            if self.protocol == "EWS" and item:
                                                self.delete_mail(status=isdelmail, index=j, ews_mail=item)
                                            self.delete_mail(status=isdelmail, index=j)
                                        logging.info(f"{j} deleted:")
                                        continue
                                    logging.info(f"Mail date:{msg_tmp.receive_time}")
                                    if self.protocol == "COREMAIL" or self.protocol == "EML":
                                        self.delete_mail(status=isdelmail, index=rawmail_id,
                                                         email_time=msg_tmp.receive_time)
                                    else:
                                        if self.protocol == "EWS" and item:
                                            self.delete_mail(status=isdelmail, index=j,
                                                             email_time=msg_tmp.receive_time, ews_mail=item)
                                        self.delete_mail(status=isdelmail, index=j, email_time=msg_tmp.receive_time)
                                    # 从服务器中解析获取邮件信息的时候，生成了新文件，需要删除这些文件
                                    self.delete_file(tmp_name, RECV_CONF['receive-setting'].get('rawmail_path'))
                                    self.delete_file(tmp_name, RECV_CONF['receive-setting'].get('rawattach_path'))
                            else:
                                if self.protocol == "EWS":
                                    item.is_read = True
                                    item.save(update_fields=['is_read'])
                                    logging.info("设置当前邮件为已读状态")
                                logging.info(f"IsDelMail:{isdelmail}, 不删除邮件!")
                        except Exception as err:
                            logging.info(traceback.format_exc())
                            logging.error(f"CONTINUE:从邮箱中删除邮件（No.{j}）发生异常，ERROR: {err}")
                            ReceiveDao.update_rawmaillog_xinfo(rawmaillog.get('ID'), -10)
                            continue
                        logging.info(f"<===== while loop {loop_num + 1} end =====>")
                        # endregion
        except Exception as err:
            logging.info(traceback.format_exc())
            logging.error(f"An error occurred while receiving mails. Error:{err}")

        return msg_list

    def import_mails(self, datas):
        try:
            if len(datas) > 0:
                import_service = DocImportService(name="import")
                for _msg in datas:
                    logging.info('************ START: Import ************')
                    import_service.import_doc(_msg)
                    logging.info('************ END: Import ************')
            self.handle_docs_file(RECV_CONF['receive-setting'].get('rawmail_path'),
                                  RECV_CONF['receive-setting'].get('cache_file_count'))
            self.handle_docs_file(RECV_CONF['receive-setting'].get('rawattach_path'),
                                  RECV_CONF['receive-setting'].get('cache_file_count'))
        except Exception as err:
            logging.info(traceback.format_exc())
            logging.error(f"An error occurred while importing mails. Error:{err}")


def get_threads():
    """
    根据recv_threads配置项匹配thread_id字段，对要接收的邮箱进行分类
    :return 需要接收处理的‘thread id’邮箱列表
    """
    recv_threads_config = RECV_CONF["receive-setting"].get("recv_threads")
    thread_sql = f"select distinct threadid from {table_km_customerinfo}"
    thread_li = RECV_DB.search(thread_sql)
    logging.info(f"km_customerinfo表存在threadid字段：{[i[0] for i in thread_li]}")
    recv_threads = []
    for t_id in thread_li:
        if t_id[0] not in recv_threads_config:
            logging.warning(f"threadid:{t_id[0]} 不在配置项（recv_threads）范围内：{recv_threads_config}，不予接收处理！")
        else:
            recv_threads.append(t_id[0])
    return recv_threads


def receive_service_work(thread_id):
    logging.info("启动接收服务...")
    # 对RECV_CONF中的相关配置添加缺省值
    if not RECV_CONF['receive-setting'].get('recv_protocol'):
        RECV_CONF['receive-setting']['recv_protocol'] = 'POP3'
    if not RECV_CONF['receive-setting'].get('cache_file_count'):
        RECV_CONF['receive-setting']['cache_file_count'] = 30
    if not RECV_CONF['receive-setting'].get('save_body_image'):
        RECV_CONF['receive-setting']['save_body_image'] = False
    # 检查rawmail_path和rawattach_path是否存在，若不存在就新建
    if not RECV_CONF['receive-setting'].get('rawmail_path'):
        RECV_CONF['receive-setting']['rawmail_path'] = Path('docs/rawmail')
    if not RECV_CONF['receive-setting'].get('rawattach_path'):
        RECV_CONF['receive-setting']['rawattach_path'] = Path('docs/rawattach')
    check_directory_exist(RECV_CONF['receive-setting'].get('rawmail_path'))
    check_directory_exist(RECV_CONF['receive-setting'].get('rawattach_path'))
    # 获取km_customerinfo表的邮箱相关配置
    sql = f"select * from {table_km_customerinfo} where threadid={thread_id}"
    mailbox_configs = deal_date(RECV_DB.search(sql, cols_info=True))
    mailbox_count = len(mailbox_configs)
    if mailbox_count < 1:
        logging.warning(f"当前threadid:{thread_id} 邮箱配置数据为空")
        return 0
    logging.info(f"当前threadid:{thread_id} 存在{mailbox_count}条邮箱配置数据")
    for m_config in mailbox_configs:
        logging.info('************ START: Receive ************')
        recv_serve = ReceiveService(name="receive", thread_id=thread_id, mailbox_config=m_config)
        mail_datas = recv_serve.recv_mails()
        if mail_datas == "continue":
            continue
        elif mail_datas == "break":
            break
        recv_serve.import_mails(mail_datas)
        logging.info('************ END: Receive ************')
        time.sleep(1)

    return 1
